package com.imooc.bilibili.service.impl;

import com.imooc.bilibili.dao.*;
import com.imooc.bilibili.entity.*;
import com.imooc.bilibili.entity.page.PageResult;
import com.imooc.bilibili.exception.ConditionException;
import com.imooc.bilibili.service.UserCoinsService;
import com.imooc.bilibili.service.UserService;
import com.imooc.bilibili.service.VideoService;
import com.imooc.bilibili.utils.FastDFSUtils;
import com.imooc.bilibili.utils.IpUtils;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericItemPreferenceArray;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service("videoService")
public class VideoServiceImpl implements VideoService {

    @Autowired
    private VideoInfoDao videoInfoDao;

    @Autowired
    private VideoTagRealtionDao videoTagRealtionDao;

    @Autowired
    private FastDFSUtils dfsUtils;

    @Autowired
    private VideoLikeInfoDao videoLikeInfoDao;

    @Autowired
    private VideoCollectionInfoDao videoCollectionInfoDao;

    @Autowired
    private CollectionGroupInfoDao collectionGroupInfoDao;

    @Autowired
    private UserCoinsService userCoinsService;

    @Autowired
    private VideoCoinsInfoDao videoCoinsInfoDao;

    @Autowired
    private UserService userService;

    @Autowired
    private VideoViewDao videoViewDao;

    @Autowired
    private VideoOperationDao videoOperationDao;

    @Transactional
    @Override
    public void insertVideoService(VideoInfo videoInfo) {
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());

        videoInfo.setEnableState(0);
        videoInfo.setDeleteState(0);
        videoInfo.setCreateTime(timestamp);
        videoInfo.setUpdateTime(timestamp);
        videoInfoDao.insert(videoInfo);
        Long videoId = videoInfo.getVideoId();

        List<VideoTagRealtion> relationList = videoInfo.getVideoTageList();
        relationList.stream().forEach(item -> {
            item.setVideoId(videoId);
            item.setEnableState(0);
            item.setDeleteState(0);
            item.setCreateTime(timestamp);
            item.setUpdateTime(timestamp);
        });
        videoTagRealtionDao.insertOrUpdateBatch(relationList);
    }

    @Transactional
    @Override
    public PageResult<VideoInfo> pageListVideos(Integer size, Integer no, String area) {
        if (null == size || null != no) {
            throw new ConditionException("参数没问题");
        }
        Map<String, Object> params = new HashMap<>();
        params.put("start", (no - 1) * size);
        params.put("limit", size);
        params.put("area", area);

        List<VideoInfo> videoInfos = new ArrayList<>();
        Integer total = videoInfoDao.pageCountVideos(params);
        if (total > 0) {
            videoInfos = videoInfoDao.pageListVideos(params);
        }
        return new PageResult<>(total, videoInfos);
    }

    @Override
    public void viewVedioOnlineSilicesService(HttpServletRequest request, HttpServletResponse response, String url) throws Exception {
        dfsUtils.viewVedioOnlineSilices(request, response, url);
    }

    @Override
    public void insertVideoLikeService(Long videoId, Long userId) {
        //根据视频id查询视频数据是否存在
        VideoInfo videoInfo = videoInfoDao.queryVideoInfoById(videoId);
        if (null == videoId) {
            throw new ConditionException("非法视频");
        }
        VideoLikeInfo videoLikeInfo = videoLikeInfoDao.queryVideoLikeByIds(userId, videoId);
        if (null != videoLikeInfo) {
            throw new ConditionException("已经点赞过了");
        }
        VideoLikeInfo likeInfo = new VideoLikeInfo();
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        likeInfo.setVideoId(videoId);
        likeInfo.setUserId(userId);
        likeInfo.setEnableState(0);
        likeInfo.setDeleteState(0);
        likeInfo.setCreateTime(timestamp);
        likeInfo.setUpdateTime(timestamp);
        videoLikeInfoDao.insert(likeInfo);
    }

    @Override
    public void delteVideoLikeService(Long videoId, Long userId) {
        videoLikeInfoDao.deleteVideoLikeByIds(videoId, userId);
    }

    @Override
    public Map<String, Object> getVideoLikeNumService(Long videoId, Long userId) {
        Long count = videoLikeInfoDao.count(videoId);
        VideoLikeInfo videoLikeInfo = videoLikeInfoDao.queryVideoLikeByIds(userId, videoId);
        boolean like = videoLikeInfo != null;
        Map<String, Object> result = new HashMap<>();
        result.put("count", count);
        result.put("like", like);
        return result;
    }

    @Override
    public void insertVideoCollectionService(VideoCollectionInfo collectionInfo) {
        //参数校验
        Long videoId = collectionInfo.getVideoId();
        Long collectionGroupId = collectionInfo.getCollectionGroupId();
        if (null == videoId || null == collectionGroupId) {
            throw new ConditionException("参数异常！");
        }
        VideoInfo videoInfo = videoInfoDao.queryVideoInfoById(videoId);
        if (null == videoInfo) {
            throw new ConditionException("非法视频");
        }
        //先删除、再添加，完成对视频收藏的修改
        videoCollectionInfoDao.deleteVideoCollectionById(collectionInfo.getUserId(), videoId);
        //添加视频收藏
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        collectionInfo.setEnableState(0);
        collectionInfo.setDeleteState(0);
        collectionInfo.setCreateTime(timestamp);
        collectionInfo.setUpdateTime(timestamp);
        videoCollectionInfoDao.insert(collectionInfo);
    }

    @Override
    public void deleteVideoColletionService(Long userId, Long videoId) {
        videoCollectionInfoDao.deleteVideoCollectionById(userId, videoId);
    }

    @Override
    public Map<String, Object> getVideoCollectionNumService(Long videoId, Long userId) {
        Long count = videoCollectionInfoDao.queryVideoCollectionCountDao(videoId);
        VideoCollectionInfo videoCollectionInfo = videoCollectionInfoDao.queryVideoCollectionByIds(userId, videoId);
        boolean collection = videoCollectionInfo != null;
        Map<String, Object> map = new HashMap<>();
        map.put("count", count);
        map.put("collection", collection);
        return map;
    }

    @Transactional
    @Override
    public void insertVideoCoinsService(VideoCoinsInfo videoCoinsInfo) {
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        Long videoId = videoCoinsInfo.getVideoId();
        Integer count = videoCoinsInfo.getCoinsAcount();
        if (null == videoId) {
            throw new ConditionException("参数异常");
        }
        VideoInfo video = videoInfoDao.queryVideoInfoById(videoId);
        if (null == video) {
            throw new ConditionException("非法视频");
        }
        //查询当前用户的硬币数量
        Integer userCoinsAmount = userCoinsService.getUserCoinsAmount(videoCoinsInfo.getUserId());
        userCoinsAmount = userCoinsAmount == null ? 0 : userCoinsAmount;
        if (count > userCoinsAmount) {
            throw new ConditionException("硬币数量不足");
        }
        //查询当前登录用户对该视频已经投了多少硬币
        VideoCoinsInfo dbCoins = videoCoinsInfoDao.getVideoCoinsByIds(videoCoinsInfo.getUserId(), videoId);
        if (null == dbCoins) {
            //如果查询的投币数据为空，新建投币数据
            videoCoinsInfo.setEnableState(0);
            videoCoinsInfo.setDeleteState(0);
            videoCoinsInfo.setCreateTime(timestamp);
            videoCoinsInfo.setUpdateTime(timestamp);
            videoCoinsInfoDao.insert(videoCoinsInfo);
        } else {
            Integer dbAmount = dbCoins.getCoinsAcount();
            dbAmount += count;
            videoCoinsInfo.setCoinsAcount(dbAmount);
            videoCoinsInfo.setUpdateTime(timestamp);
            videoCoinsInfoDao.update(videoCoinsInfo);
        }
        //更新用户当前硬币数量
        userCoinsService.updateUserCoinsAmount(videoCoinsInfo.getUserId(), (userCoinsAmount - count));
    }

    @Override
    public Map<String, Object> queryVideoCoinsCountService(Long userId, Long videoId) {
        Long count = videoCoinsInfoDao.getVideoCoinsByVideoId(videoId);
        VideoCoinsInfo videoCoinsInfo = videoCoinsInfoDao.getVideoConisInfoByIds(userId, videoId);
        boolean like = videoCoinsInfo != null;
        Map<String, Object> map = new HashMap<>();
        map.put("count", count);
        map.put("like", like);
        return map;
    }

    @Override
    public Map<String, Object> getVideoDetailsService(Long videoId) {
        VideoInfo videoInfo = videoInfoDao.queryVideoInfoById(videoId);
        Long userId = videoInfo.getUserId();
        UserInfo userInfo = userService.getUserInfoService(userId);
        Map<String, Object> result = new HashMap<>();
        result.put("video", videoInfo);
        result.put("user", userInfo);
        return result;
    }

    @Override
    public void insertVideoViewService(VideoView view, HttpServletRequest request) {
        //获取用户id和视频id
        Long userId = view.getUserId();
        Long videoId = view.getVideoId();
        //生成clientId
        String agent = request.getHeader("User-Agent");
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        String clientId = String.valueOf(userAgent.getId());
        String ip = IpUtils.getIP(request);
        Map<String, Object> params = new HashMap<>();
        if (null != userId) {
            //存在userId，身份就是用户
            params.put("userId", userId);
        } else {
            //不存在userId，身份就是游客
            params.put("clientId", clientId);
            params.put("ip", ip);
        }
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        params.put("today", sdf.format(timestamp));
        params.put("videoId", videoId);

        //添加观看记录前，判断是否已经添加过了
        VideoView videoViewInfo = videoViewDao.queryVideoViewInfoDao(params);
        if (null == videoViewInfo) {
            view.setClientId(clientId);
            view.setUserIp(ip);
            view.setEnableState(0);
            view.setDeleteState(0);
            view.setUpdateTime(timestamp);
            videoViewDao.insert(view);
        }
    }

    @Override
    public Integer queryVideoViewCountsServicec(Long videoId) {
        return videoViewDao.queryVideoViewCountByVideoId(videoId);
    }

    @Override
    public List<VideoInfo> recommendService(Long userId) throws TasteException {
        //查询用户偏好得分
        List<UserReference> referenceList = videoOperationDao.queryAllUserReference();
        //创建数据模型
        DataModel dataModel = this.createDataModel(referenceList);
        //获取用户的相似度
        UserSimilarity similarity = new UncenteredCosineSimilarity(dataModel);
        //获取用户资源,基于用户进行内容推荐
        UserNeighborhood userNeighborhood = new NearestNUserNeighborhood(2,similarity,dataModel);
        long[] ar = userNeighborhood.getUserNeighborhood(userId);
        //构建推荐器
        Recommender recommender = new GenericUserBasedRecommender(dataModel,userNeighborhood,similarity);
        //推荐商品,数字参数代表是推荐给用的物品数量
        List<RecommendedItem> recommendedItems = recommender.recommend(userId,5);
        List<Long> itemIds = recommendedItems.stream().map(RecommendedItem::getItemID).collect(Collectors.toList());
        //根据视频id进行批量查询
        return videoInfoDao.batchGetVideoByRecommendIds(itemIds);
    }

    /**
     * 将用数据库中查询出来的偏好数据与Mahout提供的偏好数据实体类进行映射
     *
     * @param referenceList
     * @return
     */
    private DataModel createDataModel(List<UserReference> referenceList) {
        FastByIDMap<PreferenceArray> fastByIDMap = new FastByIDMap<>();
        Map<Long, List<UserReference>> map = referenceList.stream().collect(Collectors.groupingBy(UserReference::getUserId));
        Collection<List<UserReference>> lists = map.values();

        Iterator<List<UserReference>> iterator = lists.iterator();
        while (iterator.hasNext()) {
            List<UserReference> next = iterator.next();
            GenericPreference[] array = new GenericPreference[next.size()];
            for (int i = 0; i < next.size(); i++) {
                UserReference userReference = next.get(i);
                GenericPreference genericPreference = new GenericPreference(userReference.getUserId(), userReference.getVideoId(), userReference.getValue());
                array[i] = genericPreference;
            }
            fastByIDMap.put(array[0].getUserID(), new GenericItemPreferenceArray(Arrays.asList(array)));
        }
        return new GenericDataModel(fastByIDMap);
    }
}
